﻿#include "FITKPolyDataNormals.h"

#include "vtkCellArray.h"
#include "vtkCellData.h"
#include "vtkFloatArray.h"
#include "vtkIdList.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkMath.h"
#include "vtkObjectFactory.h"
#include "vtkPointData.h"
#include "vtkPolyData.h"
#include "vtkPolygon.h"
#include "vtkPriorityQueue.h"
#include "vtkTriangleStrip.h"

vtkStandardNewMacro(FITKPolyDataNormals);

// Construct with feature angle=30, splitting and consistency turned on,
// flipNormals turned off, and non-manifold traversal turned on.
FITKPolyDataNormals::FITKPolyDataNormals()
{
    this->FeatureAngle = 30.0;
    this->Splitting = 1;
    this->Consistency = 1;
    this->FlipNormals = 0;
    this->ComputePointNormals = 1;
    this->ComputeCellNormals = 0;
    this->NonManifoldTraversal = 1;
    this->AutoOrientNormals = 0;
    // some internal data
    this->NumFlips = 0;
    this->OutputPointsPrecision = vtkAlgorithm::DEFAULT_PRECISION;
    this->Wave = nullptr;
    this->Wave2 = nullptr;
    this->CellIds = nullptr;
    this->CellPoints = nullptr;
    this->NeighborPoints = nullptr;
    this->Map = nullptr;
    this->OldMesh = nullptr;
    this->NewMesh = nullptr;
    this->Visited = nullptr;
    this->PolyNormals = nullptr;
    this->CosAngle = 0.0;
}

#define VTK_CELL_NOT_VISITED 0
#define VTK_CELL_VISITED 1

// Generate normals for polygon meshes
int FITKPolyDataNormals::RequestData(vtkInformation* vtkNotUsed(request),
    vtkInformationVector** inputVector, vtkInformationVector* outputVector)
{
    // get the info objects
    vtkInformation* inInfo = inputVector[0]->GetInformationObject(0);
    vtkInformation* outInfo = outputVector->GetInformationObject(0);

    // get the input and output
    vtkPolyData* input = vtkPolyData::SafeDownCast(inInfo->Get(vtkDataObject::DATA_OBJECT()));
    vtkPolyData* output = vtkPolyData::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT()));

    vtkIdType npts = 0;
#if VTK_MAJOR_VERSION < 9
    vtkIdType* pts = nullptr; 
#else
    const vtkIdType* pts = nullptr;
#endif

    vtkIdType numNewPts;
    double flipDirection = 1.0;
    vtkIdType numVerts, numLines, numPolys, numStrips;
    vtkIdType cellId;
    vtkIdType numPts;
    vtkPoints* inPts;
    vtkCellArray *inPolys, *inStrips, *polys;
    vtkPoints* newPts = nullptr;
    vtkFloatArray* newNormals;
    vtkPointData *pd, *outPD;
    vtkDataSetAttributes* outCD = output->GetCellData();
    double n[3];
    vtkCellArray* newPolys;
    vtkIdType ptId, oldId;

    vtkDebugMacro(<< "Generating surface normals");

    numVerts = input->GetNumberOfVerts();
    numLines = input->GetNumberOfLines();
    numPolys = input->GetNumberOfPolys();
    numStrips = input->GetNumberOfStrips();
    if ((numPts = input->GetNumberOfPoints()) < 1)
    {
        vtkDebugMacro(<< "No data to generate normals for!");
        return 1;
    }

    // If there is nothing to do, pass the data through
    if ((this->ComputePointNormals == 0 && this->ComputeCellNormals == 0) ||
        (numPolys < 1 && numStrips < 1))
    { // don't do anything! pass data through
        output->CopyStructure(input);
        output->GetPointData()->PassData(input->GetPointData());
        output->GetCellData()->PassData(input->GetCellData());
        return 1;
    }

    if (numStrips < 1)
    {
        output->GetCellData()->PassData(input->GetCellData());
    }

    // Load data into cell structure.  We need two copies: one is a
    // non-writable mesh used to perform topological queries.  The other
    // is used to write into and modify the connectivity of the mesh.
    //
    inPts = input->GetPoints();
    inPolys = input->GetPolys();
    inStrips = input->GetStrips();

    this->OldMesh = vtkPolyData::New();
    this->OldMesh->SetPoints(inPts);
    if (numStrips > 0) // have to decompose strips into triangles
    {
        vtkDataSetAttributes* inCD = input->GetCellData();
        // When we have triangle strips, make sure to create and copy
        // the cell data appropriately. Since strips are broken into
        // triangles, cell data cannot be passed as it is and needs to
        // be copied tuple by tuple.
        outCD->CopyAllocate(inCD);
        if (numPolys > 0)
        {
            polys = vtkCellArray::New();
            polys->DeepCopy(inPolys);
            vtkSmartPointer<vtkIdList> ids = vtkSmartPointer<vtkIdList>::New();
            ids->SetNumberOfIds(numPolys);
            for (vtkIdType i = 0; i < numPolys; i++)
            {
                ids->SetId(i, i);
            }
            outCD->CopyData(inCD, ids, ids);
        }
        else
        {
            polys = vtkCellArray::New();

#if VTK_MAJOR_VERSION < 9
            polys->Allocate(polys->EstimateSize(numStrips, 5));
#else
            polys->AllocateEstimate(numStrips, 5);
#endif          
        }
        vtkIdType inCellIdx = numPolys;
        vtkIdType outCellIdx = numPolys;
        for (inStrips->InitTraversal(); inStrips->GetNextCell(npts, pts); inCellIdx++)
        {
            vtkTriangleStrip::DecomposeStrip(npts, pts, polys);
            // Copy the cell data for the strip to each triangle.
            for (vtkIdType i = 0; i < npts - 2; i++)
            {
                outCD->CopyData(inCD, inCellIdx, outCellIdx++);
            }
        }
        this->OldMesh->SetPolys(polys);
        polys->Delete();
        numPolys = polys->GetNumberOfCells(); // added some new triangles
    }
    else
    {
        this->OldMesh->SetPolys(inPolys);
        polys = inPolys;
    }
    this->OldMesh->BuildLinks();
    this->UpdateProgress(0.10);

    pd = input->GetPointData();
    outPD = output->GetPointData();

    this->NewMesh = vtkPolyData::New();
    this->NewMesh->SetPoints(inPts);
    // create a copy because we're modifying it
    newPolys = vtkCellArray::New();
    newPolys->DeepCopy(polys);
    this->NewMesh->SetPolys(newPolys);
    this->NewMesh->BuildCells(); // builds connectivity

    // The visited array keeps track of which polygons have been visited.
    //
    if (this->Consistency || this->Splitting || this->AutoOrientNormals)
    {
        this->Visited = new int[numPolys];
        memset(this->Visited, VTK_CELL_NOT_VISITED, numPolys * sizeof(int));
        this->CellIds = vtkIdList::New();
        this->CellIds->Allocate(VTK_CELL_SIZE);
        this->CellPoints = vtkIdList::New();
        this->CellPoints->Allocate(VTK_CELL_SIZE);
        this->NeighborPoints = vtkIdList::New();
        this->NeighborPoints->Allocate(VTK_CELL_SIZE);
    }
    else
    {
        this->Visited = nullptr;
    }

    //  Traverse all polygons insuring proper direction of ordering.  This
    //  works by propagating a wave from a seed polygon to the polygon's
    //  edge neighbors. Each neighbor may be reordered to maintain consistency
    //  with its (already checked) neighbors.
    //
    this->NumFlips = 0;
    if (this->AutoOrientNormals)
    {
        // No need to check this->Consistency. It's implied.

        // Ok, here's the basic idea: the "left-most" polygon should
        // have its outward pointing normal facing left. If it doesn't,
        // reverse the vertex order. Then use it as the seed for other
        // connected polys. To find left-most polygon, first find left-most
        // point, and examine neighboring polys and see which one
        // has a normal that's "most aligned" with the X-axis. This process
        // will need to be repeated to handle all connected components in
        // the mesh. Report bugs/issues to cvolpe@ara.com.
        int foundLeftmostCell;
        vtkIdType leftmostCellID = -1, currentPointID, currentCellID;
        vtkIdType* leftmostCells;       
        
#if VTK_MAJOR_VERSION < 9
        unsigned short nleftmostCells;
        vtkIdType* cellPts;
#else  
        vtkIdType nleftmostCells;
        const vtkIdType* cellPts;
#endif

        vtkIdType nCellPts;
        int cIdx;
        double bestNormalAbsXComponent;
        int bestReverseFlag;
        vtkPriorityQueue* leftmostPoints = vtkPriorityQueue::New();
        this->Wave = vtkIdList::New();
        this->Wave->Allocate(numPolys / 4 + 1, numPolys);
        this->Wave2 = vtkIdList::New();
        this->Wave2->Allocate(numPolys / 4 + 1, numPolys);

        // Put all the points in the priority queue, based on x coord
        // So that we can find leftmost point
        leftmostPoints->Allocate(numPts);
        for (ptId = 0; ptId < numPts; ptId++)
        {
            leftmostPoints->Insert(inPts->GetPoint(ptId)[0], ptId);
        }

        // Repeat this while loop as long as the queue is not empty,
        // because there may be multiple connected components, each of
        // which needs to be seeded independently with a correctly
        // oriented polygon.
        while (leftmostPoints->GetNumberOfItems())
        {
            foundLeftmostCell = 0;
            // Keep iterating through leftmost points and cells located at
            // those points until I've got a leftmost point with
            // unvisited cells attached and I've found the best cell
            // at that point
            do
            {
                currentPointID = leftmostPoints->Pop();
                this->OldMesh->GetPointCells(currentPointID, nleftmostCells, leftmostCells);
                bestNormalAbsXComponent = 0.0;
                bestReverseFlag = 0;
                for (cIdx = 0; cIdx < nleftmostCells; cIdx++)
                {
                    currentCellID = leftmostCells[cIdx];
                    if (this->Visited[currentCellID] == VTK_CELL_VISITED)
                    {
                        continue;
                    }
                    this->OldMesh->GetCellPoints(currentCellID, nCellPts, cellPts);
                    vtkPolygon::ComputeNormal(inPts, nCellPts, cellPts, n);
                    // Ok, see if this leftmost cell candidate is the best
                    // so far
                    if (fabs(n[0]) > bestNormalAbsXComponent)
                    {
                        bestNormalAbsXComponent = fabs(n[0]);
                        leftmostCellID = currentCellID;
                        // If the current leftmost cell's normal is pointing to the
                        // right, then the vertex ordering is wrong
                        bestReverseFlag = (n[0] > 0);
                        foundLeftmostCell = 1;
                    } // if this normal is most x-aligned so far
                }   // for each cell at current leftmost point
            } while (leftmostPoints->GetNumberOfItems() && !foundLeftmostCell);
            if (foundLeftmostCell)
            {
                // We've got the seed for a connected component! But do
                // we need to flip it first? We do, if it was pointed the wrong
                // way to begin with, or if the user requested flipping all
                // normals, but if both are true, then we leave it as it is.
                if (bestReverseFlag ^ this->FlipNormals)
                {
                    this->NewMesh->ReverseCell(leftmostCellID);
                    this->NumFlips++;
                }
                this->Wave->InsertNextId(leftmostCellID);
                this->Visited[leftmostCellID] = VTK_CELL_VISITED;
                this->TraverseAndOrder();
                this->Wave->Reset();
                this->Wave2->Reset();
            } // if found leftmost cell
        }   // Still some points in the queue
        this->Wave->Delete();
        this->Wave2->Delete();
        leftmostPoints->Delete();
        vtkDebugMacro(<< "Reversed ordering of " << this->NumFlips << " polygons");
    } // automatically orient normals
    else
    {
        if (this->Consistency)
        {
            this->Wave = vtkIdList::New();
            this->Wave->Allocate(numPolys / 4 + 1, numPolys);
            this->Wave2 = vtkIdList::New();
            this->Wave2->Allocate(numPolys / 4 + 1, numPolys);
            for (cellId = 0; cellId < numPolys; cellId++)
            {
                if (this->Visited[cellId] == VTK_CELL_NOT_VISITED)
                {
                    if (this->FlipNormals)
                    {
                        this->NumFlips++;
                        this->NewMesh->ReverseCell(cellId);
                    }
                    this->Wave->InsertNextId(cellId);
                    this->Visited[cellId] = VTK_CELL_VISITED;
                    this->TraverseAndOrder();
                }

                this->Wave->Reset();
                this->Wave2->Reset();
            }

            this->Wave->Delete();
            this->Wave2->Delete();
            vtkDebugMacro(<< "Reversed ordering of " << this->NumFlips << " polygons");
        } // Consistent ordering
    }   // don't automatically orient normals

    this->UpdateProgress(0.333);

    //  Initial pass to compute polygon normals without effects of neighbors
    //
    this->PolyNormals = vtkFloatArray::New();
    this->PolyNormals->SetNumberOfComponents(3);
    this->PolyNormals->SetName("Normals");
    this->PolyNormals->SetNumberOfTuples(numVerts + numLines + numPolys);

    vtkIdType offsetCells = numVerts + numLines;
    n[0] = 1.0;
    n[1] = 0.0;
    n[2] = 0.0;
    for (cellId = 0; cellId < offsetCells; cellId++)
    {
        // add a default value for vertices and lines
        // normals do not have meaningful values, we set them to X
        this->PolyNormals->SetTuple(cellId, n);
    }

    // Modified by CHT
    //@{
    for (cellId = 0; cellId < this->OldMesh->GetNumberOfCells(); cellId++)
    {
        if ((cellId % 1000) == 0)
        {
            this->UpdateProgress(0.333 + 0.333 * (double)cellId / (double)numPolys);
            if (this->GetAbortExecute())
            {
                break;
            }
        }

        vtkPolygon::ComputeNormal(this->OldMesh->GetCell(cellId)->GetPoints(), n);
        this->PolyNormals->SetTuple(offsetCells + cellId, n);
    }
    //@}

    //for (cellId = 0, newPolys->InitTraversal(); newPolys->GetNextCell(npts, pts); cellId++)
    //{
    //    if ((cellId % 1000) == 0)
    //    {
    //        this->UpdateProgress(0.333 + 0.333 * (double)cellId / (double)numPolys);
    //        if (this->GetAbortExecute())
    //        {
    //            break;
    //        }
    //    }

    //    vtkPolygon::ComputeNormal(inPts, npts, pts, n);
    //    this->PolyNormals->SetTuple(offsetCells + cellId, n);
    //}

    // Split mesh if sharp features
    if (this->Splitting)
    {
        //  Traverse all nodes; evaluate loops and feature edges.  If feature
        //  edges found, split mesh creating new nodes.  Update polygon
        // connectivity.
        //
        this->CosAngle = cos(vtkMath::RadiansFromDegrees(this->FeatureAngle));
        //  Splitting will create new points.  We have to create index array
        // to map new points into old points.
        //
        this->Map = vtkIdList::New();
        this->Map->SetNumberOfIds(numPts);
        for (vtkIdType i = 0; i < numPts; i++)
        {
            this->Map->SetId(i, i);
        }

        for (ptId = 0; ptId < numPts; ptId++)
        {
            this->MarkAndSplit(ptId);
        } // for all input points

        numNewPts = this->Map->GetNumberOfIds();

        vtkDebugMacro(<< "Created " << numNewPts - numPts << " new points");

        //  Now need to map attributes of old points into new points.
        //
        outPD->CopyNormalsOff();
        outPD->CopyAllocate(pd, numNewPts);

        newPts = vtkPoints::New();

        // set precision for the points in the output
        if (this->OutputPointsPrecision == vtkAlgorithm::DEFAULT_PRECISION)
        {
            vtkPointSet* inputPointSet = vtkPointSet::SafeDownCast(input);
            if (inputPointSet)
            {
                newPts->SetDataType(inputPointSet->GetPoints()->GetDataType());
            }
            else
            {
                newPts->SetDataType(VTK_FLOAT);
            }
        }
        else if (this->OutputPointsPrecision == vtkAlgorithm::SINGLE_PRECISION)
        {
            newPts->SetDataType(VTK_FLOAT);
        }
        else if (this->OutputPointsPrecision == vtkAlgorithm::DOUBLE_PRECISION)
        {
            newPts->SetDataType(VTK_DOUBLE);
        }

        newPts->SetNumberOfPoints(numNewPts);
        for (ptId = 0; ptId < numNewPts; ptId++)
        {
            oldId = this->Map->GetId(ptId);
            newPts->SetPoint(ptId, inPts->GetPoint(oldId));
            outPD->CopyData(pd, oldId, ptId);
        }
        this->Map->Delete();
    } // splitting

    else // no splitting, so no new points
    {
        numNewPts = numPts;
        outPD->CopyNormalsOff();
        outPD->PassData(pd);
    }

    if (this->Consistency || this->Splitting)
    {
        delete[] this->Visited;
        this->CellIds->Delete();
        this->CellIds = nullptr;
        this->CellPoints->Delete();
        this->CellPoints = nullptr;
        this->NeighborPoints->Delete();
        this->NeighborPoints = nullptr;
    }

    this->UpdateProgress(0.80);

    //  Finally, traverse all elements, computing polygon normals and
    //  accumulating them at the vertices.
    //
    if (this->FlipNormals && !this->Consistency)
    {
        flipDirection = -1.0;
    }

    newNormals = vtkFloatArray::New();
    newNormals->SetNumberOfComponents(3);
    newNormals->SetNumberOfTuples(numNewPts);
    newNormals->SetName("Normals");
    float* fNormals = newNormals->WritePointer(0, 3 * numNewPts);
    std::fill_n(fNormals, 3 * numNewPts, 0);

    float* fPolyNormals = this->PolyNormals->WritePointer(3 * offsetCells, 3 * numPolys);

    if (this->ComputePointNormals)
    {
        for (cellId = 0, newPolys->InitTraversal(); newPolys->GetNextCell(npts, pts); ++cellId)
        {
            for (vtkIdType i = 0; i < npts; ++i)
            {
                fNormals[3 * pts[i]] += fPolyNormals[3 * cellId];
                fNormals[3 * pts[i] + 1] += fPolyNormals[3 * cellId + 1];
                fNormals[3 * pts[i] + 2] += fPolyNormals[3 * cellId + 2];
            }
        }

        for (vtkIdType i = 0; i < numNewPts; ++i)
        {
            const double length =
                sqrt(fNormals[3 * i] * fNormals[3 * i] + fNormals[3 * i + 1] * fNormals[3 * i + 1] +
                    fNormals[3 * i + 2] * fNormals[3 * i + 2]) *
                flipDirection;
            if (length != 0.0)
            {
                fNormals[3 * i] /= length;
                fNormals[3 * i + 1] /= length;
                fNormals[3 * i + 2] /= length;
            }
        }
    }

    //  Update ourselves.  If no new nodes have been created (i.e., no
    //  splitting), we can simply pass data through.
    //
    if (!this->Splitting)
    {
        output->SetPoints(inPts);
    }

    //  If there is splitting, then have to send down the new data.
    //
    else
    {
        output->SetPoints(newPts);
        newPts->Delete();
    }

    if (this->ComputeCellNormals)
    {
        outCD->SetNormals(this->PolyNormals);
    }
    this->PolyNormals->Delete();

    if (this->ComputePointNormals)
    {
        outPD->SetNormals(newNormals);
    }
    newNormals->Delete();

    output->SetPolys(newPolys);
    newPolys->Delete();

    // copy the original vertices and lines to the output
    output->SetVerts(input->GetVerts());
    output->SetLines(input->GetLines());

    this->OldMesh->Delete();
    this->NewMesh->Delete();

    return 1;
}

//  Propagate wave of consistently ordered polygons.
//
void FITKPolyDataNormals::TraverseAndOrder()
{
    vtkIdType i, k;
    int j, l, j1;
    vtkIdType numIds, cellId;
    const vtkIdType* pts;
    const vtkIdType* neiPts;
    vtkIdType npts;
    vtkIdType numNeiPts;
    vtkIdType neighbor;
    vtkIdList* tmpWave;

    // propagate wave until nothing left in wave
    while ((numIds = this->Wave->GetNumberOfIds()) > 0)
    {
        for (i = 0; i < numIds; i++)
        {
            cellId = this->Wave->GetId(i);

            // Store the results here in a vtkIdList, since passing npts/pts directly
            // would result in the data getting invalidated by the later call to
            // NewMesh->GetCellPoints.
            this->NewMesh->GetCellPoints(cellId, this->CellPoints);
            npts = this->CellPoints->GetNumberOfIds();
            pts = this->CellPoints->GetPointer(0);

            for (j = 0, j1 = 1; j < npts; ++j, (j1 = (++j1 < npts) ? j1 : 0)) // for each edge neighbor
            {
                this->OldMesh->GetCellEdgeNeighbors(cellId, pts[j], pts[j1], this->CellIds);

                //  Check the direction of the neighbor ordering.  Should be
                //  consistent with us (i.e., if we are n1->n2,
                // neighbor should be n2->n1).
                if (this->CellIds->GetNumberOfIds() == 1 || this->NonManifoldTraversal)
                {
                    for (k = 0; k < this->CellIds->GetNumberOfIds(); k++)
                    {
                        if (this->Visited[this->CellIds->GetId(k)] == VTK_CELL_NOT_VISITED)
                        {
                            neighbor = this->CellIds->GetId(k);
                            this->NewMesh->GetCellPoints(neighbor, this->NeighborPoints);
                            numNeiPts = this->NeighborPoints->GetNumberOfIds();
                            neiPts = this->NeighborPoints->GetPointer(0);

                            for (l = 0; l < numNeiPts; l++)
                            {
                                if (neiPts[l] == pts[j1])
                                {
                                    break;
                                }
                            }

                            //  Have to reverse ordering if neighbor not consistent
                            //
                            if (neiPts[(l + 1) % numNeiPts] != pts[j])
                            {
                                this->NumFlips++;
                                this->NewMesh->ReverseCell(neighbor);
                            }
                            this->Visited[neighbor] = VTK_CELL_VISITED;
                            this->Wave2->InsertNextId(neighbor);
                        } // if cell not visited
                    }   // for each edge neighbor
                }     // for manifold or non-manifold traversal allowed
            }       // for all edges of this polygon
        }         // for all cells in wave

        // swap wave and proceed with propagation
        tmpWave = this->Wave;
        this->Wave = this->Wave2;
        this->Wave2 = tmpWave;
        this->Wave2->Reset();
    } // while wave still propagating
}

//
//  Mark polygons around vertex.  Create new vertex (if necessary) and
//  replace (i.e., split mesh).
//
void FITKPolyDataNormals::MarkAndSplit(vtkIdType ptId)
{
    int i, j;

    // Get the cells using this point and make sure that we have to do something
    
#if VTK_MAJOR_VERSION < 9
    unsigned short ncells;
#else
    vtkIdType ncells;    
#endif

    vtkIdType* cells;

    this->OldMesh->GetPointCells(ptId, ncells, cells);
    if (ncells <= 1)
    {
        return; // point does not need to be further disconnected
    }

    // Start moving around the "cycle" of points using the point. Label
    // each point as requiring a visit. Then label each subregion of cells
    // connected to this point that are connected (and not separated by
    // a feature edge) with a given region number. For each N regions
    // created, N-1 duplicate (split) points are created. The split point
    // replaces the current point ptId in the polygons connectivity array.
    //
    // Start by initializing the cells as unvisited
    for (i = 0; i < ncells; i++)
    {
        this->Visited[cells[i]] = -1;
    }

    // Loop over all cells and mark the region that each is in.
    //
#if VTK_MAJOR_VERSION < 9
    vtkIdType* pts;
#else  
    const vtkIdType* pts;
#endif

    vtkIdType numPts;
    
    int numRegions = 0;
    vtkIdType spot, neiPt[2], nei, cellId, neiCellId;
    double thisNormal[3], neiNormal[3];
    for (j = 0; j < ncells; j++) // for all cells connected to point
    {
        if (this->Visited[cells[j]] < 0) // for all unvisited cells
        {
            this->Visited[cells[j]] = numRegions;
            // okay, mark all the cells connected to this seed cell and using ptId
            this->OldMesh->GetCellPoints(cells[j], numPts, pts);
            // find the two edges
            for (spot = 0; spot < numPts; spot++)
            {
                if (pts[spot] == ptId)
                {
                    break;
                }
            }

            if (spot == 0)
            {
                neiPt[0] = pts[spot + 1];
                neiPt[1] = pts[numPts - 1];
            }
            else if (spot == (numPts - 1))
            {
                neiPt[0] = pts[spot - 1];
                neiPt[1] = pts[0];
            }
            else
            {
                neiPt[0] = pts[spot + 1];
                neiPt[1] = pts[spot - 1];
            }

            for (i = 0; i < 2; i++) // for each of the two edges of the seed cell
            {
                cellId = cells[j];
                nei = neiPt[i];
                while (cellId >= 0) // while we can grow this region
                {
                    this->OldMesh->GetCellEdgeNeighbors(cellId, ptId, nei, this->CellIds);
                    if (this->CellIds->GetNumberOfIds() == 1 &&
                        this->Visited[(neiCellId = this->CellIds->GetId(0))] < 0)
                    {
                        this->PolyNormals->GetTuple(cellId, thisNormal);
                        this->PolyNormals->GetTuple(neiCellId, neiNormal);

                        if (vtkMath::Dot(thisNormal, neiNormal) > CosAngle)
                        {
                            // visit and arrange to visit next edge neighbor
                            this->Visited[neiCellId] = numRegions;
                            cellId = neiCellId;
                            this->OldMesh->GetCellPoints(cellId, numPts, pts);

                            for (spot = 0; spot < numPts; spot++)
                            {
                                if (pts[spot] == ptId)
                                {
                                    break;
                                }
                            }

                            if (spot == 0)
                            {
                                nei = (pts[spot + 1] != nei ? pts[spot + 1] : pts[numPts - 1]);
                            }
                            else if (spot == (numPts - 1))
                            {
                                nei = (pts[spot - 1] != nei ? pts[spot - 1] : pts[0]);
                            }
                            else
                            {
                                nei = (pts[spot + 1] != nei ? pts[spot + 1] : pts[spot - 1]);
                            }

                        } // if not separated by edge angle
                        else
                        {
                            cellId = -1; // separated by edge angle
                        }
                    } // if can move to edge neighbor
                    else
                    {
                        cellId = -1; // separated by previous visit, boundary, or non-manifold
                    }
                } // while visit wave is propagating
            }   // for each of the two edges of the starting cell
            numRegions++;
        } // if cell is unvisited
    }   // for all cells connected to point ptId

    if (numRegions <= 1)
    {
        return; // a single region, no splitting ever required
    }

    // Okay, for all cells not in the first region, the ptId is
    // replaced with a new ptId, which is a duplicate of the first
    // point, but disconnected topologically.
    //
    vtkIdType lastId = this->Map->GetNumberOfIds();
    vtkIdType replacementPoint;
    for (j = 0; j < ncells; j++)
    {
        if (this->Visited[cells[j]] > 0) // replace point if splitting needed
        {
            replacementPoint = lastId + this->Visited[cells[j]] - 1;
            this->Map->InsertId(replacementPoint, ptId);
            this->NewMesh->ReplaceCellPoint(cells[j], ptId, replacementPoint);
        } // if not in first regions and requiring splitting
    }   // for all cells connected to ptId
}

void FITKPolyDataNormals::PrintSelf(ostream& os, vtkIndent indent)
{
    this->Superclass::PrintSelf(os, indent);

    os << indent << "Feature Angle: " << this->FeatureAngle << "\n";
    os << indent << "Splitting: " << (this->Splitting ? "On\n" : "Off\n");
    os << indent << "Consistency: " << (this->Consistency ? "On\n" : "Off\n");
    os << indent << "Flip Normals: " << (this->FlipNormals ? "On\n" : "Off\n");
    os << indent << "Auto Orient Normals: " << (this->AutoOrientNormals ? "On\n" : "Off\n");
    os << indent << "Num Flips: " << this->NumFlips << endl;
    os << indent << "Compute Point Normals: " << (this->ComputePointNormals ? "On\n" : "Off\n");
    os << indent << "Compute Cell Normals: " << (this->ComputeCellNormals ? "On\n" : "Off\n");
    os << indent << "Non-manifold Traversal: " << (this->NonManifoldTraversal ? "On\n" : "Off\n");
    os << indent << "Precision of the output points: " << this->OutputPointsPrecision << "\n";
}
